/**************************************************************************
 * Copyright (C) 2017-2017  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : hb_housekeeper.c
 * Author      : wufangfang@unisound.com
 * Date        : 2020.04.13
 *
 **************************************************************************/
#include "unione.h"
#include "user_asr.h"
#include "user_player.h"
#include "user_uart_hb1688.h"
#include "user_flash.h"
#include "user_uni_ucp.h"
#include "ArcLib.h"

#define TAG "hb_housekeeoer"

#define KEY_WORD_LEN_MAX    64
#define REPLAY_MATCH_OK     "116"  // 匹配成功
#define REPLAY_MATCH_FAIL   "117"  // 匹配失败
#define REPLAY_LEARN_OK     "119"  // 学习成功
#define REPLAY_LEARN_FAIL   "120"  // 学习失败
#define REPLAY_CODE_OK      "187"  // 对码成功
#define REPLAY_CODE_FAIL    "188"  // 对码失败

#define BLOB_KEY_IS_MATCH   "AC_IS_MATCH"   // save in flash key, value means 0: nerver match AC RC, 1: have matched
#define BLOB_TYPE_IS_MATCH  uni_u32
#define BLOB_KEY_AC_TYPE    "AC_TYPE"       // save in flash key, value means number get AC RC code from code lib
#define BLOB_TYPE_AC_TYPE   int
#define BLOB_KEY_ADR_VAR    "ADR_VAR"

#define AC_TMP_MIN          16
#define AC_TMP_MAX          32

typedef enum {
  HB1688_LEARN_NONE = 0,
  HB1688_AC_MATCH,
  HB1688_LEARN_ARC,
  HB1688_LEARN_URC,
  HB1688_LEARN_CODE,
  HB1688_LEARN_MAX
}HB1688_LEARN_MODE;

typedef enum {
  HB1688_ADR_MODE = 0,
  HB1688_ADR_TMP,
  HB1688_ADR_FANLEV,
  HB1688_ADR_FANDIR,
  HB1688_ADR_AFANDIR,
  HB1688_ADR_MFANDIR,
  HB1688_ADR_NONE_06,
  HB1688_ADR_TIMEON,
  HB1688_ADR_TIMEOFF,
  HB1688_ADR_KEYVAL,
  HB1688_ADR_SYSFLAG,
  HB1688_ADR_NONE_11,
  HB1688_ADR_TMSTATE3,
  HB1688_ADR_NONE_13,
  HB1688_ADR_NONE_14,
  HB1688_ADR_NONE_15,
  HB1688_ADR_MAX
}HB1688_ADR_TYPE;

typedef struct {
  char              key_word[KEY_WORD_LEN_MAX];
  uni_u8            code[HB1688_LRC_LEN];
  uni_u8            len;
}hb1688_lrc_code_t;

typedef struct {
  uni_bool          is_learn;
  HB1688_LEARN_MODE learn_mode;
  hb1688_lrc_code_t lrc_code;
}hb_housekeeper_context_t;

typedef struct {
  const char  *cmd_str;
  uni_bool    is_block;
  uni_u8      cmd_num_h;
  uni_u8      cmd_num_l;
}mcu_cmd_map_t;

static uni_bool g_speaker_mute = false;
static hb_housekeeper_context_t g_hk_context = {false, HB1688_LEARN_NONE, {{0}, {0}, 0}};
static unsigned char g_adr_var[HB1688_ADR_MAX] = {0};
static mcu_cmd_map_t g_mcu_cmd_map[] = {
  { "volumeMaxUni",    true, 0x00, 0x00 },
  { "volumeMidUni",    true, 0x00, 0x01 },
  { "volumeMinUni",    true, 0x00, 0x02 },
  { "speakerMute",     true, 0x00, 0x03 },
  { "speakerUnmMute",  true, 0x00, 0x04 },
  { "learnMode",       true, 0x00, 0x05 },
  { "learnModeExit",   true, 0x00, 0x06 },
  { "userGoOut",       true, 0x00, 0x07 },
  { "userGoHome",      true, 0x00, 0x08 },
  { "userGoToBed",     true, 0x00, 0x09 },
  { "userGetUp",       true, 0x00, 0x0A },
  { "rcAcMatch",       true, 0x00, 0x0B },
  { "acTurnOn",        true, 0x00, 0x0C },
  { "acTurnOff",       true, 0x00, 0x0D },
  { "acModeCool",      true, 0x00, 0x0E },
  { "acModeHeat",      true, 0x00, 0x0F },
  { "acModeBlower",    true, 0x00, 0x10 },
  { "acModeMoisture",  true, 0x00, 0x11 },
  { "acFanSpeedHigh",  true, 0x00, 0x12 },
  { "acFanSpeedMid",   true, 0x00, 0x13 },
  { "acFanSpeedLow",   true, 0x00, 0x14 },
  { "acFanSpeedAuto",  true, 0x00, 0x15 },
  { "acFanSweptOn",    true, 0x00, 0x16 },
  { "acFanSweptOff",   true, 0x00, 0x17 },
  { "acDegrees16",     true, 0x00, 0x18 },
  { "acDegrees17",     true, 0x00, 0x19 },
  { "acDegrees18",     true, 0x00, 0x1A },
  { "acDegrees19",     true, 0x00, 0x1B },
  { "acDegrees20",     true, 0x00, 0x1C },
  { "acDegrees21",     true, 0x00, 0x1D },
  { "acDegrees22",     true, 0x00, 0x1E },
  { "acDegrees23",     true, 0x00, 0x1F },
  { "acDegrees24",     true, 0x00, 0x20 },
  { "acDegrees25",     true, 0x00, 0x21 },
  { "acDegrees26",     true, 0x00, 0x22 },
  { "acDegrees27",     true, 0x00, 0x23 },
  { "acDegrees28",     true, 0x00, 0x24 },
  { "acDegrees29",     true, 0x00, 0x25 },
  { "acDegrees30",     true, 0x00, 0x26 },
  { "rcLearnKey",      true, 0x00, 0x27 },
  { "tvTurnOn",        true, 0x00, 0x28 },
  { "tvTurnOff",       true, 0x00, 0x29 },
  { "tvChannelPre",    true, 0x00, 0x2A },
  { "tvChannelNext",   true, 0x00, 0x2B },
  { "tvVolumeUp",      true, 0x00, 0x2C },
  { "tvVolumeDown",    true, 0x00, 0x2D },
  { "tvSpeakerMute",   true, 0x00, 0x2E },
  { "tvSpeakerUnmute", true, 0x00, 0x2F },
  { "tvSettings",      true, 0x00, 0x30 },
  { "tvMenu",          true, 0x00, 0x31 },
  { "tvEnter",         true, 0x00, 0x32 },
  { "tvReturn",        true, 0x00, 0x33 },
  { "tvSwitchLeft",    true, 0x00, 0x34 },
  { "tvSwitchRight",   true, 0x00, 0x35 },
  { "tvSwitchUp",      true, 0x00, 0x36 },
  { "tvSwitchDown",    true, 0x00, 0x37 },
  { "tvSwicthSingle",  true, 0x00, 0x38 },
  { "stbTurnOn",       true, 0x00, 0x39 },
  { "stbTurnOff",      true, 0x00, 0x3A },
  { "lampLivingOn",    true, 0x00, 0x3B },
  { "lampLivingOff",   true, 0x00, 0x3C },
  { "downLightOn",     true, 0x00, 0x3D },
  { "downLightOff",    true, 0x00, 0x3E },
  { "tapeLightOn",     true, 0x00, 0x3F },
  { "tapeLightOff",    true, 0x00, 0x40 },
  { "lampDiningOn",    true, 0x00, 0x41 },
  { "lampDiningOff",   true, 0x00, 0x42 },
  { "lampBedroomOn",   true, 0x00, 0x43 },
  { "lampBedroomOff",  true, 0x00, 0x44 },
  { "lampBedsideOn",   true, 0x00, 0x45 },
  { "lampBedsideOff",  true, 0x00, 0x46 },
  { "lampNightOn",     true, 0x00, 0x47 },
  { "lampNightOff",    true, 0x00, 0x48 },
  { "lampAllOn",       true, 0x00, 0x49 },
  { "lampAllOff",      true, 0x00, 0x4A },
  { "curtainsOpen",    true, 0x00, 0x4B },
  { "curtainsClose",   true, 0x00, 0x4C },
  { "curtainsCancel",  true, 0x00, 0x4D },
  { "codeLampLiving",  true, 0x00, 0x4E },
  { "codeDownLight",   true, 0x00, 0x4F },
  { "codeTapeLight",   true, 0x00, 0x50 },
  { "codeLampDining",  true, 0x00, 0x51 },
  { "codeLampBedroom", true, 0x00, 0x52 },
  { "codeLampBedside", true, 0x00, 0x53 },
  { "codeLampNight",   true, 0x00, 0x54 },
  { "codeCurtains",    true, 0x00, 0x55 }
};

static int _send_cmd_uart_protocol(const char *cmd) {
  int i = 0;
  uni_u16 ucp_cmd = 0x0000;
  for (i = 0; i < sizeof(g_mcu_cmd_map)/sizeof(mcu_cmd_map_t); i++) {
    if (0 == uni_strcmp(cmd, g_mcu_cmd_map[i].cmd_str)) {
      LOGT(TAG, "UART protcol: %s : (%s) 0x%02x [0x%02x]",
                g_mcu_cmd_map[i].cmd_str, g_mcu_cmd_map[i].is_block ? "block" : "unblock",
                g_mcu_cmd_map[i].cmd_num_h, g_mcu_cmd_map[i].cmd_num_l);
      ucp_cmd  = g_mcu_cmd_map[i].cmd_num_h;
      ucp_cmd  = (ucp_cmd  << 8) | g_mcu_cmd_map[i].cmd_num_l;
      return user_uni_ucp_send(ucp_cmd, NULL, 0, g_mcu_cmd_map[i].is_block);
    }
  }
  return 0;
}

static void _ext_dev_ucp_recv(uni_u16 cmd, char *payload, int len) {
  if (0x0056 == cmd) {
    /* code learn success */
    user_player_play(AUDIO_PLAY_REPLY, REPLAY_CODE_OK);
  } else if (0x0057 == cmd) {
    /* code learn failed */
    user_player_play(AUDIO_PLAY_REPLY, REPLAY_CODE_FAIL);
  }
}

static void _hb1688_lrc_learn_recv(uni_bool is_ok, uni_u8 *buf, uni_u8 len) {
  BLOB_TYPE_IS_MATCH is_ac_match = 0;
  BLOB_TYPE_AC_TYPE ac_type = 0;
  unsigned int ac_group_num[10] = {0};
  switch (g_hk_context.learn_mode) {
    case HB1688_AC_MATCH:
      if (is_ok) {
        /* match use HB1688 lib*/
        Arc_Identification((unsigned const char *)buf, ac_group_num, 0x01);
        LOGT(TAG, "ac_group_num = %d", ac_group_num[0]);
        if (ac_group_num[0] > 0) {
          /* save match type to flash */
          is_ac_match = 1;
          ac_type = ac_group_num[0];
          user_flash_set_env_blob(BLOB_KEY_IS_MATCH, &is_ac_match,
                                  sizeof(is_ac_match));
          user_flash_set_env_blob(BLOB_KEY_AC_TYPE, &ac_type,
                                  sizeof(BLOB_TYPE_AC_TYPE));
          user_player_play(AUDIO_PLAY_REPLY, REPLAY_MATCH_OK);
        } else {
          user_player_play(AUDIO_PLAY_REPLY, REPLAY_MATCH_FAIL);
        }
      } else {
        user_player_play(AUDIO_PLAY_REPLY, REPLAY_MATCH_FAIL);
      }
      break;
    case HB1688_LEARN_ARC:
      if (is_ok) {
        is_ac_match = 0;
        user_flash_set_env_blob(BLOB_KEY_IS_MATCH, &is_ac_match,
                                sizeof(is_ac_match));
        user_flash_set_env_blob(g_hk_context.lrc_code.key_word, buf, len);
        user_player_play(AUDIO_PLAY_REPLY, REPLAY_LEARN_OK);
      } else {
        user_player_play(AUDIO_PLAY_REPLY, REPLAY_LEARN_FAIL);
      }
      break;
    case HB1688_LEARN_URC:
      if (is_ok) {
        user_flash_set_env_blob(g_hk_context.lrc_code.key_word, buf, len);
        user_player_play(AUDIO_PLAY_REPLY, REPLAY_LEARN_OK);
      } else {
        user_player_play(AUDIO_PLAY_REPLY, REPLAY_LEARN_FAIL);
      }
      break;
    default:
      break;
  }
}

static int _speaker_mute_process(event_custom_setting_t *setting) {
  if (0 == uni_strcmp(setting->cmd, "speakerMute")) {
    if (0 == _send_cmd_uart_protocol(setting->cmd)) {
      g_speaker_mute = true;
      user_player_reply_list_random(setting->reply_files);
    } else {
      user_player_speaker_mute();
    }
  } else {
    g_speaker_mute = false;
    user_player_speaker_unmute();
    if (0 == _send_cmd_uart_protocol(setting->cmd)) {
      user_player_reply_list_random(setting->reply_files);
    }
  }
  return 0;
}

static void _goto_ac_match_mode(void) {
  switch (g_hk_context.learn_mode) {
    case HB1688_LEARN_CODE:
      /* TODO: exit code learn mode, don't support on HB-M */
      break;
    default:
      break;
  }
  g_hk_context.lrc_code.len = 0;
  g_hk_context.learn_mode = HB1688_AC_MATCH;
}

static void _goto_arc_learn_mode(void) {
  switch (g_hk_context.learn_mode) {
    case HB1688_LEARN_CODE:
      /* TODO: exit code learn mode, don't support on HB-M */
      break;
    default:
      break;
  }
  g_hk_context.lrc_code.len = 0;
  g_hk_context.learn_mode = HB1688_LEARN_ARC;
}

static void _goto_urc_learn_mode(void) {
  switch (g_hk_context.learn_mode) {
    case HB1688_LEARN_CODE:
      /* TODO: exit code learn mode, don't support on HB-M */
      break;
    default:
      break;
  }
  g_hk_context.lrc_code.len = 0;
  g_hk_context.learn_mode = HB1688_LEARN_URC;
}

static void _goto_code_learn_mode(void) {
  switch (g_hk_context.learn_mode) {
    case HB1688_AC_MATCH:
      user_uart_hb1688_learn_exit();
      break;
    case HB1688_LEARN_ARC:
      user_uart_hb1688_learn_exit();
      break;
    case HB1688_LEARN_URC:
      user_uart_hb1688_learn_exit();
      break;
    default:
      break;
  }
  g_hk_context.lrc_code.len = 0;
  g_hk_context.learn_mode = HB1688_LEARN_CODE;
}

static void _enter_learn_mode(void) {
  LOGT(TAG, "enter learn_mode: %d", g_hk_context.learn_mode);
  switch (g_hk_context.learn_mode) {
    case HB1688_AC_MATCH:
      user_uart_hb1688_arc_learn(_hb1688_lrc_learn_recv);;
      break;
    case HB1688_LEARN_ARC:
      user_uart_hb1688_arc_learn(_hb1688_lrc_learn_recv);;
      break;
    case HB1688_LEARN_URC:
      user_uart_hb1688_urc_learn(_hb1688_lrc_learn_recv);;
      break;
    case HB1688_LEARN_CODE:
      /* TODO: enter code learn mode, don't support on HB-M */
      break;
    default:
      break;
  }
}

static void _exit_learn_mode(void) {
  if (HB1688_LEARN_NONE != g_hk_context.learn_mode) {
    user_uart_hb1688_learn_exit();
    /* TODO: exit code learn mode, don't support on HB-M */
    g_hk_context.lrc_code.len = 0;
    g_hk_context.learn_mode = HB1688_LEARN_NONE;
  }
}

static int _learn_mode_process(event_custom_setting_t *setting) {
  if (0 == uni_strcmp(setting->cmd, "learnModeExit")) {
    _exit_learn_mode();
    g_hk_context.is_learn = false;
  } else {
    g_hk_context.is_learn = true;
    g_hk_context.learn_mode = HB1688_LEARN_NONE;
  }
  if (0 == _send_cmd_uart_protocol(setting->cmd)) {
    user_player_reply_list_random(setting->reply_files);
  }
  return 0;
}

static int _work_mode_process(event_custom_setting_t *setting) {
  if (0 == _send_cmd_uart_protocol(setting->cmd)) {
    user_player_reply_list_random(setting->reply_files);
  }
  return 0;
}

static int _code_learn_mode_process(event_custom_setting_t *setting) {
  if (!g_hk_context.is_learn) {
    LOGE(TAG, "It's not on learn mode.");
    return -1;
  }
  _goto_code_learn_mode();
  if (0 == _send_cmd_uart_protocol(setting->cmd)) {
    user_player_reply_list_num(setting->reply_files, 0);
  }
  return 0;
}

static int _lrc_learn_mode_process(event_custom_setting_t *setting) {
  if (!g_hk_context.is_learn) {
    LOGE(TAG, "It's not on learn mode.");
    return -1;
  }
  /* default to URC learn mode, will switch to ARC mode if user say AC command */
  _goto_urc_learn_mode();
  if (0 == _send_cmd_uart_protocol(setting->cmd)) {
    user_player_reply_list_num(setting->reply_files, 0);
  }
  return 0;
}

static int _ac_match_process(event_custom_setting_t *setting) {
  if (!g_hk_context.is_learn) {
    LOGE(TAG, "It's not on learn mode.");
    return -1;
  }
  _goto_ac_match_mode();
  _enter_learn_mode();
  if (0 == _send_cmd_uart_protocol(setting->cmd)) {
    user_player_reply_list_num(setting->reply_files, 0);
  }
  return 0;
}

static int _ac_key_learn_process(event_custom_setting_t *setting) {
  /* default to URC learn mode, will switch to ARC mode if user say AC command */
  if (HB1688_LEARN_ARC != g_hk_context.learn_mode
    && HB1688_LEARN_URC != g_hk_context.learn_mode) {
    LOGE(TAG, "not in HB1688_LEARN_ARC or HB1688_LEARN_URC mode.");
    return -1;
  }
  uni_strncpy(g_hk_context.lrc_code.key_word, setting->cmd,
              sizeof(g_hk_context.lrc_code.key_word) - 1);
  LOGT(TAG, "key_word: %s", g_hk_context.lrc_code.key_word);
  _goto_arc_learn_mode();
  _enter_learn_mode();
  /* don't send protocol to MCU on learn mode */
  user_player_reply_list_num(setting->reply_files, 0);
  return 0;
}

static void _adr_var_ac_tmp_set(unsigned char tmp) {
  if (g_adr_var[HB1688_ADR_TMP] < (tmp - AC_TMP_MIN)) {
    g_adr_var[HB1688_ADR_KEYVAL] = 1;
  } else if (g_adr_var[HB1688_ADR_TMP] > (tmp - AC_TMP_MIN)) {
    g_adr_var[HB1688_ADR_KEYVAL] = 2;
  } else {
    g_adr_var[HB1688_ADR_KEYVAL] = 0xff;
  }
  g_adr_var[HB1688_ADR_TMP] = (tmp - AC_TMP_MIN);
}

static int _flush_adr_var(const char *cmd) {
  if (0 == strcmp(cmd, "acTurnOn")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 0;  //电源键
    g_adr_var[HB1688_ADR_SYSFLAG] = 0x20 & g_adr_var[HB1688_ADR_SYSFLAG]; //清除灯光以外的所有功能标志
    g_adr_var[HB1688_ADR_SYSFLAG] = 0x01 | g_adr_var[HB1688_ADR_SYSFLAG]; //开机标志
  } else if (0 == strcmp(cmd, "acTurnOff")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 0;   //电源键
    g_adr_var[HB1688_ADR_SYSFLAG] = 0x20 & g_adr_var[HB1688_ADR_SYSFLAG]; //清除灯光以外的所有功能标志
  } else if (0 == strcmp(cmd, "acModeCool")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 3;   //模式键
    g_adr_var[HB1688_ADR_MODE] = 1;     //制冷模式
  } else if (0 == strcmp(cmd, "acModeHeat")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 3;   //模式键
    g_adr_var[HB1688_ADR_MODE] = 4;     //制热模式
  } else if (0 == strcmp(cmd, "acModeBlower")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 3;   //模式键
    g_adr_var[HB1688_ADR_MODE] = 3;     //送风模式
  } else if (0 == strcmp(cmd, "acModeMoisture")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 3;   //模式键
    g_adr_var[HB1688_ADR_MODE] = 2;     //除湿模式
  } else if (0 == strcmp(cmd, "acFanSpeedHigh")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 4;   //风量键
    g_adr_var[HB1688_ADR_FANLEV] = 3;   //高速风
  } else if (0 == strcmp(cmd, "acFanSpeedMid")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 4;   //风量键
    g_adr_var[HB1688_ADR_FANLEV] = 2;   //中速风
  } else if (0 == strcmp(cmd, "acFanSpeedLow")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 4;   //风量键
    g_adr_var[HB1688_ADR_FANLEV] = 1;   //低速风
  } else if (0 == strcmp(cmd, "acFanSpeedAuto")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 4;   //风量键
    g_adr_var[HB1688_ADR_FANLEV] = 0;   //自动风
  } else if (0 == strcmp(cmd, "acFanSweptOn")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 5;   //自动风向
    if (g_adr_var[HB1688_ADR_FANDIR] < 9) {
      g_adr_var[HB1688_ADR_FANDIR] += 1;
    } else {
      g_adr_var[HB1688_ADR_FANDIR] = 0;
    }
    g_adr_var[HB1688_ADR_AFANDIR] = g_adr_var[HB1688_ADR_FANDIR];
    g_adr_var[HB1688_ADR_MFANDIR] = 0;  //清除手动风向
  } else if (0 == strcmp(cmd, "acFanSweptOff")) {
    g_adr_var[HB1688_ADR_KEYVAL] = 6;   //手动风向
    if (g_adr_var[HB1688_ADR_FANDIR] < 9) {
      g_adr_var[HB1688_ADR_FANDIR] += 1;
    } else {
      g_adr_var[HB1688_ADR_FANDIR] = 0;
    }
    g_adr_var[HB1688_ADR_AFANDIR] = 0;  //清除自动风向
    g_adr_var[HB1688_ADR_MFANDIR] = g_adr_var[HB1688_ADR_FANDIR];
  } else if (0 == strcmp(cmd, "acDegrees16")) {
    _adr_var_ac_tmp_set(16);
  } else if (0 == strcmp(cmd, "acDegrees17")) {
    _adr_var_ac_tmp_set(17);
  } else if (0 == strcmp(cmd, "acDegrees18")) {
    _adr_var_ac_tmp_set(18);
  } else if (0 == strcmp(cmd, "acDegrees19")) {
    _adr_var_ac_tmp_set(19);
  } else if (0 == strcmp(cmd, "acDegrees20")) {
    _adr_var_ac_tmp_set(20);
  } else if (0 == strcmp(cmd, "acDegrees21")) {
    _adr_var_ac_tmp_set(21);
  } else if (0 == strcmp(cmd, "acDegrees22")) {
    _adr_var_ac_tmp_set(22);
  } else if (0 == strcmp(cmd, "acDegrees23")) {
    _adr_var_ac_tmp_set(23);
  } else if (0 == strcmp(cmd, "acDegrees24")) {
    _adr_var_ac_tmp_set(24);
  } else if (0 == strcmp(cmd, "acDegrees25")) {
    _adr_var_ac_tmp_set(25);
  } else if (0 == strcmp(cmd, "acDegrees26")) {
    _adr_var_ac_tmp_set(26);
  } else if (0 == strcmp(cmd, "acDegrees27")) {
    _adr_var_ac_tmp_set(27);
  } else if (0 == strcmp(cmd, "acDegrees28")) {
    _adr_var_ac_tmp_set(28);
  } else if (0 == strcmp(cmd, "acDegrees29")) {
    _adr_var_ac_tmp_set(29);
  } else if (0 == strcmp(cmd, "acDegrees30")) {
    _adr_var_ac_tmp_set(30);
  } else {
    LOGE(TAG, "unknown cmd: %s", cmd);
    return -1;
  }
  return 0;
}

static int _ac_key_use_process(event_custom_setting_t *setting) {
  BLOB_TYPE_IS_MATCH is_ac_match = 0;
  BLOB_TYPE_AC_TYPE ac_type = 0;
  int save_len = 0;
  /* send protocol in any case */
  if (0 == _send_cmd_uart_protocol(setting->cmd)) {
    user_player_reply_list_num(setting->reply_files, 0);
  }
  /* is AC match or not */
  user_flash_get_env_blob(BLOB_KEY_IS_MATCH, &is_ac_match,
                          sizeof(is_ac_match), &save_len);
  if (save_len != sizeof(is_ac_match)) {
    LOGE(TAG, "cannot found %s in flash", BLOB_KEY_IS_MATCH);
    return -1;
  }
  if (is_ac_match) {
    user_flash_get_env_blob(BLOB_KEY_AC_TYPE, &ac_type,
                            sizeof(BLOB_TYPE_AC_TYPE), &save_len);
    if (save_len != sizeof(BLOB_TYPE_AC_TYPE)) {
      LOGE(TAG, "cannot found %s in flash", BLOB_KEY_AC_TYPE);
      return -1;
    }
    /* map cmd and ARC var */
    user_flash_get_env_blob(BLOB_KEY_ADR_VAR, g_adr_var,
                              sizeof(g_adr_var), &save_len);
    if (save_len != sizeof(g_adr_var)) {
      LOGE(TAG, "cannot found %s in flash.", BLOB_KEY_ADR_VAR);
      return -1;
    }
    if (0 == _flush_adr_var(setting->cmd)) {
      /* get ARC code from code lib and send to UART */
      Read_ARC_IR_Data(ac_type, g_adr_var, 0x01);
      user_flash_set_env_blob(BLOB_KEY_ADR_VAR, g_adr_var, sizeof(g_adr_var));
      user_uart_hb1688_arc_send(ArcIRdataBuff, HB1688_LRC_LEN);
    } else {
      LOGE(TAG, "cannot map cmd: %s", setting->cmd);
    }
  } else {
    /* find LRC code from flash by cmd */
    user_flash_get_env_blob(setting->cmd, g_hk_context.lrc_code.code,
                            sizeof(g_hk_context.lrc_code.code),
                            &save_len);
    if (save_len <= 0) {
      LOGE(TAG, "cannot found %s in flash", setting->cmd);
      return -1;
    }
    if (save_len > sizeof(g_hk_context.lrc_code.code)) {
      save_len = sizeof(g_hk_context.lrc_code.code);
    }
    g_hk_context.lrc_code.len = (uni_u8)save_len;
    user_uart_hb1688_arc_send(g_hk_context.lrc_code.code,
                              g_hk_context.lrc_code.len);
  }
  return 0;
}

static int _ac_command_process(event_custom_setting_t *setting) {
  LOGT(TAG, "is_learn = %d", g_hk_context.is_learn);
  if (g_hk_context.is_learn) {
    return _ac_key_learn_process(setting);
  } else {
    return _ac_key_use_process(setting);
  }
}

static int _tv_key_learn_process(event_custom_setting_t *setting) {
  /* default to URC learn mode, will switch to ARC mode if user say AC command */
  if (HB1688_LEARN_ARC != g_hk_context.learn_mode
    && HB1688_LEARN_URC != g_hk_context.learn_mode) {
    LOGE(TAG, "not in HB1688_LEARN_ARC or HB1688_LEARN_URC mode.");
    return -1;
  }
  uni_strncpy(g_hk_context.lrc_code.key_word, setting->cmd,
              sizeof(g_hk_context.lrc_code.key_word) - 1);
  _goto_urc_learn_mode();
  _enter_learn_mode();
  /* don't send protocol to MCU on learn mode */
  user_player_reply_list_num(setting->reply_files, 0);
  return 0;
}

static int _tv_key_use_process(event_custom_setting_t *setting) {
  int save_len = 0;
  /* send protocol in any case */
  if (0 == _send_cmd_uart_protocol(setting->cmd)) {
    user_player_reply_list_num(setting->reply_files, 0);
  }
  /* find LRC code from flash by cmd */
  user_flash_get_env_blob(setting->cmd, g_hk_context.lrc_code.code,
                          sizeof(g_hk_context.lrc_code.code),
                          &save_len);
  if (save_len <= 0) {
    LOGE(TAG, "cannot found %s in flash", setting->cmd);
    return -1;
  }
  if (save_len > sizeof(g_hk_context.lrc_code.code)) {
    save_len = sizeof(g_hk_context.lrc_code.code);
  }
  g_hk_context.lrc_code.len = (uni_u8)save_len;
  user_uart_hb1688_urc_send(g_hk_context.lrc_code.code,
                            g_hk_context.lrc_code.len);
  return 0;
}

static int _tv_command_process(event_custom_setting_t *setting) {
  if (g_hk_context.is_learn) {
    return _tv_key_learn_process(setting);
  } else {
    return _tv_key_use_process(setting);
  }
}

static int _stb_command_process(event_custom_setting_t *setting) {
  if (g_hk_context.is_learn) {
    return _tv_key_learn_process(setting);
  } else {
    return _tv_key_use_process(setting);
  }
}

static void _custom_setting_cb(USER_EVENT_TYPE event,
                                     user_event_context_t *context) {
  event_custom_setting_t *setting = NULL;
  if (context) {
    setting = &context->custom_setting;
    LOGT(TAG, "user command: %s", setting->cmd);
    if (0 == uni_strncmp(setting->cmd, "speaker", uni_strlen("speaker"))) {
      _speaker_mute_process(setting);
    } else if (0 == uni_strncmp(setting->cmd, "learnMode", uni_strlen("learnMode"))) {
      _learn_mode_process(setting);
    } else if (0 == uni_strncmp(setting->cmd, "user", uni_strlen("user"))) {
      _work_mode_process(setting);
    } else if (0 == uni_strncmp(setting->cmd, "ac", uni_strlen("ac"))) {
      _ac_command_process(setting);
    } else if (0 == uni_strncmp(setting->cmd, "tv", uni_strlen("tv"))) {
      _tv_command_process(setting);
    } else if (0 == uni_strncmp(setting->cmd, "stb", uni_strlen("stb"))) {
      _stb_command_process(setting);
    } else if (0 == uni_strncmp(setting->cmd, "code", uni_strlen("code"))) {
      _code_learn_mode_process(setting);
    } else if (0 == uni_strcmp(setting->cmd, "rcLearnKey")) {
      _lrc_learn_mode_process(setting);
    } else if (0 == uni_strcmp(setting->cmd, "rcAcMatch")) {
      _ac_match_process(setting);
    } else {
      if (0 == _send_cmd_uart_protocol(setting->cmd)) {
        user_player_reply_list_random(setting->reply_files);
      }
    }
  }
}

 static void _volume_setting_cb(USER_EVENT_TYPE event,
                                      user_event_context_t *context) {
  event_volume_setting_t *setting = NULL;
  if (context) {
    setting = &context->voluem_setting;
    LOGT(TAG, "user command: %s", setting->cmd);
    if (0 == uni_strcmp(setting->cmd, "volumeMinUni")) {
      user_player_set_volume_min();
      if (0 == _send_cmd_uart_protocol(setting->cmd)) {
        user_player_reply_list_random(setting->reply_files);
      }
    } else if (0 == uni_strcmp(setting->cmd, "volumeMaxUni")) {
      user_player_set_volume_max();
      if (0 == _send_cmd_uart_protocol(setting->cmd)) {
        user_player_reply_list_random(setting->reply_files);
      }
    } else if (0 == uni_strcmp(setting->cmd, "volumeMidUni")) {
      user_player_set_volume_mid();
      if (0 == _send_cmd_uart_protocol(setting->cmd)) {
        user_player_reply_list_random(setting->reply_files);
      }
    } else {
      LOGE(TAG, "Unknown cmd: %s", setting->cmd);
    }
  }
}

static void _audio_play_end_cb(USER_EVENT_TYPE event,
                                    user_event_context_t *context) {
  LOGT(TAG, "g_speaker_mute = %d", g_speaker_mute);
  if (g_speaker_mute) {
    g_speaker_mute = false;
    user_player_speaker_mute();
  }
}

static void _goto_sleeping_cb (USER_EVENT_TYPE event,
                                     user_event_context_t *context) {
  event_goto_sleeping_t *sleeping = NULL;
  if (context) {
    sleeping = &context->goto_sleeping;
    user_player_reply_list_random(sleeping->reply_files);
    _exit_learn_mode();
    g_hk_context.is_learn = false;
  }
}

static void _register_event_callback(void) {
  user_event_subscribe_event(USER_CUSTOM_SETTING, _custom_setting_cb);
  user_event_subscribe_event(USER_VOLUME_SETTING, _volume_setting_cb);
  user_event_subscribe_event(USER_AUDIO_PLAY_END, _audio_play_end_cb);
  user_event_subscribe_event(USER_GOTO_SLEEPING, _goto_sleeping_cb);
}

static void _hb1688_version_recv(uni_bool is_ok, uni_u16 version) {
  uni_u8 ver_master = version >> 8;
  uni_u8 ver_major = version & 0x0f;
  if (is_ok) {
    LOGT(TAG, "HB1688 version: %d.%d", ver_master, ver_major);
  } else {
    LOGE(TAG, "Get HB1688 version failed.");
  }
}

static void _init_adr_var(void) {
  int save_len = 0;
  user_flash_get_env_blob(BLOB_KEY_ADR_VAR, g_adr_var,
                          sizeof(g_adr_var), &save_len);
  if (save_len != sizeof(g_adr_var)) {
    g_adr_var[HB1688_ADR_MODE] = 1;       //制冷模式
    g_adr_var[HB1688_ADR_TMP] = 9;        //25度
    g_adr_var[HB1688_ADR_FANLEV] = 0;     //自动
    g_adr_var[HB1688_ADR_FANDIR] = 0;     //自动风量清0（自动手动同按键情况）
    g_adr_var[HB1688_ADR_AFANDIR] = 0;    //自动风量清0（自动手动不同按键情况）
    g_adr_var[HB1688_ADR_MFANDIR] = 0;    //手动风向清0（自动手动不同按键情况）
    g_adr_var[HB1688_ADR_NONE_06] = 0;
    g_adr_var[HB1688_ADR_TIMEON] = 0;     //无定时开机
    g_adr_var[HB1688_ADR_TIMEOFF] = 0;    //无定时关机
    g_adr_var[HB1688_ADR_KEYVAL] = 0xff;  //空键
    g_adr_var[HB1688_ADR_SYSFLAG] = 0x00; //清零
    g_adr_var[HB1688_ADR_NONE_11] = 0;
    g_adr_var[HB1688_ADR_TMSTATE3] = 0;   //无定时开关机
    g_adr_var[HB1688_ADR_NONE_13] = 0;
    g_adr_var[HB1688_ADR_NONE_14] = 0;
    g_adr_var[HB1688_ADR_NONE_15] = 0;
    user_flash_set_env_blob(BLOB_KEY_ADR_VAR, g_adr_var, sizeof(g_adr_var));
  }
}

static void _init_ac_match_state(void) {
  BLOB_TYPE_IS_MATCH is_ac_match = 0;
  int save_len = 0;
  user_flash_get_env_blob(BLOB_KEY_IS_MATCH, &is_ac_match,
                          sizeof(is_ac_match), &save_len);
  if (save_len != sizeof(is_ac_match)) {
    is_ac_match = 0;
    user_flash_set_env_blob(BLOB_KEY_IS_MATCH, &is_ac_match,
                            sizeof(is_ac_match));
  }
}

int hb_housekeeper(void) {
  if (0 != user_uart_hb1688_init()) {
    LOGE(TAG, "user_uart_hb1688_init failed.");
    return -1;
  }
  if (0 != user_flash_init()) {
    LOGE(TAG, "user_flash_init failed.");
    user_uart_hb1688_final();
    return -1;
  }
  if (0 != user_uni_ucp_init(_ext_dev_ucp_recv)) {
    LOGE(TAG, "user_uni_ucp_init failed.");
    user_uart_hb1688_final();
    user_flash_final();
    return -1;
  }
  user_uart_hb1688_get_version(_hb1688_version_recv);
  _register_event_callback();
  _init_ac_match_state();
  _init_adr_var();
  return 0;
}

